
/*
	MCL - TComponent.h
	Copyright (C) 2019 CrownSoft
  
	This software is provided 'as-is', without any express or implied
	warranty.  In no event will the authors be held liable for any damages
	arising from the use of this software.

	Permission is granted to anyone to use this software for any purpose,
	including commercial applications, and to alter it and redistribute it
	freely, subject to the following restrictions:

	1. The origin of this software must not be misrepresented; you must not
	   claim that you wrote the original software. If you use this software
	   in a product, an acknowledgment in the product documentation would be
	   appreciated but is not required.
	2. Altered source versions must be plainly marked as such, and must not be
	   misrepresented as being the original software.
	3. This notice may not be removed or altered from any source distribution.
	  
*/

#pragma once

#include <windows.h>
#include "../text/TString.h"
#include "../containers/TLeakDetector.h"
#include "../eventprop.h"
#include "TMessage.h"

/**
	Base class of all W32 gui objects.
*/
class TComponent
{
protected:
	TString classNameProperty;
	TString textProperty;
	HWND handleProperty;
	HWND parentProperty;
	DWORD styleProperty;
	DWORD extendedStyleProperty;
	UINT controlID;
	int leftProperty;
	int topProperty;
	int widthProperty;
	int heightProperty;
	bool visibleProperty;
	bool enabledProperty;
	bool isRegistered;
	HFONT fontProperty;
	HCURSOR cursorProperty;

public:
	WNDCLASSEXW wc;

	PROPERTY_DEF_READONLY(TString, className, getClassName)
	PROPERTY_DEF_READONLY(HWND, handle, getHandle)
	PROPERTY_DEF(TString, text, getText, setText)
	PROPERTY_DEF(HWND, parent, getParent, setParent)
	PROPERTY_DEF(DWORD, style, getStyle, setStyle)
	PROPERTY_DEF(DWORD, extendedStyle, getExtendedStyle, setExtendedStyle)
	PROPERTY_DEF(int, left, getLeft, setLeft)
	PROPERTY_DEF(int, top, getTop, setTop)
	PROPERTY_DEF(int, width, getWidth, setWidth)
	PROPERTY_DEF(int, height, getHeight, setHeight)
	PROPERTY_DEF(bool, visible, getVisible, setVisible)
	PROPERTY_DEF(bool, enabled, getEnabled, setEnabled)
	PROPERTY_DEF(HFONT, font, getFont, setFont)
	PROPERTY_DEF(HCURSOR, cursor, getCursor, setCursor)

	// LRESULT windowProc(TMessage& message)
	EVENT_DEF(LRESULT, windowProc, TMessage&)

	// bool notifyProc(TMessage& message, LRESULT& result)
	EVENT_DEF(bool, notifyProc, TMessage&, LRESULT&)

	// void onCreate(TComponent* sender)
	EVENT_DEF(void, onCreate, TComponent*)

	// void onDestroy(TComponent* sender)
	EVENT_DEF(void, onDestroy, TComponent*)

	// void onMouseDown(TComponent* sender, int mouseButton, int x, int y)
	EVENT_DEF(void, onMouseDown, TComponent*, int, int, int)

	// void onMouseUp(TComponent* sender, int mouseButton, int x, int y)
	EVENT_DEF(void, onMouseUp, TComponent*, int, int, int)

	// void onMouseMove(TComponent* sender, int x, int y)
	EVENT_DEF(void, onMouseMove, TComponent*, int, int)

	// void onMouseLeave(TComponent* sender)
	EVENT_DEF(void, onMouseLeave, TComponent*)

	// void onMouseWheel(TComponent* sender, int wheelDelta, int x, int y)
	EVENT_DEF(void, onMouseWheel, TComponent*, int, int, int)

	// void onDblClick(TComponent* sender, int mouseButton, int x, int y)
	EVENT_DEF(void, onDblClick, TComponent*, int, int, int)

	// void onKeyPress(TComponent* sender, char key)
	EVENT_DEF(void, onKeyPress, TComponent*, char)

	// void onKeyDown(TComponent* sender, short virtualKeyCode)
	EVENT_DEF(void, onKeyDown, TComponent*, short)

	// void onKeyUp(TComponent* sender, short virtualKeyCode)
	EVENT_DEF(void, onKeyUp, TComponent*, short)

	/**
		Constructs a standard win32 component.
	*/
	TComponent();
	
	/**
		Returns HWND of this component
	*/
	operator HWND()const;

	/**
		Sets mouse cursor of this component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setCursor(HCURSOR cursorHandle) { setCursorImpl(cursorHandle); }

	/**
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline HCURSOR getCursor() { return getCursorImpl(); }

	/**
		@returns class name(autogenerated unique) for this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline TString getClassName() { return getClassNameImpl(); }

	/**
		Registers the class name and creates the component. 
		@returns false if registration failed or component creation failed.
	*/
	virtual bool create();

	virtual void destroy();

	/**
		Handles internal window messages. (subclassed window proc)
		Important: Pass unprocessed messages to parent if you override this method.
	*/
	virtual LRESULT windowProcHandler(TMessage& message);

	/**
		Receives notification messages like WM_COMMAND, WM_NOTIFY, WM_DRAWITEM from the parent window. (if it belongs to this component)
		Pass unprocessed messages to parent if you override this method.
		@returns true if msg processed.
	*/
	virtual bool notifyProcHandler(TMessage& message, LRESULT& result);

	/**
		send unprocessed messages into default window proc
	*/
	virtual LRESULT dispatchToDefaultProc(TMessage& message);

	/**
		Identifier of the child component which can be used with WM_MEASUREITEM like messages.
		@returns zero for top level windows
	*/
	virtual UINT getControlID();

	/**
		Sets font of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setFont(HFONT fontHandle) { setFontImpl(fontHandle); }

	/**
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline HFONT getFont() { return getFontImpl(); }

	/**
		Returns caption of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline TString getText() { return getTextImpl(); }

	/**
		Sets caption of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setText(const TString& caption) { setTextImpl(caption); }

	/**
		For internal use only!
	*/
	virtual void setHandle(HWND hwnd);

	/**
		Returns HWND of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline HWND getHandle() { return getHandleImpl(); }

	/**
		Changes parent of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setParent(HWND parentHandle) { setParentImpl(parentHandle); }

	/**
		Returns parent of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline HWND getParent() { return getParentImpl(); }

	/**
		Returns style of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline DWORD getStyle() { return getStyleImpl(); }

	/**
		Sets style of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setStyle(DWORD compStyle) { setStyleImpl(compStyle); }

	/**
		Returns extended style of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline DWORD getExtendedStyle() { return getExtendedStyleImpl(); }

	/**
		Sets extended style of this component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setExtendedStyle(DWORD compExStyle) { setExtendedStyleImpl(compExStyle); }

	/**
		Returns x position of this component which is relative to parent component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline int getLeft() { return getLeftImpl(); }

	/**
		Sets x position of the component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setLeft(int compLeft) { setLeftImpl(compLeft); }

	/**
		Returns y position of this component which is relative to parent component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline int getTop() { return getTopImpl(); }

	/**
		Sets y position of the component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setTop(int compTop) { setTopImpl(compTop); }

	virtual void setPosition(int compLeft, int compTop);

	/**
		Returns width of the component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline int getWidth() { return getWidthImpl(); }

	/**
		Sets width of the component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setWidth(int compWidth) { setWidthImpl(compWidth); }

	/**
		Returns height of the component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline int getHeight() { return getHeightImpl(); }

	/**
		Sets height of the component.
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setHeight(int compHeight) { setHeightImpl(compHeight); }

	virtual void setSize(int compWidth, int compHeight);

	/**
		Sets visible state of the component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setVisible(bool state) { setVisibleImpl(state); }

	/**
		Returns visible state of the component
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline bool getVisible() { return getVisibleImpl(); }

	virtual void show();

	virtual void hide();

	/**
		Returns the component is ready for user input or not
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline bool getEnabled() { return getEnabledImpl(); }

	/**
		Sets component's user input reading state
		(Please override the "Impl" method to change the behaviour!)
	*/
	inline void setEnabled(bool state) { setEnabledImpl(state); }

	/**
		Brings component to front
	*/
	virtual void bringToFront();

	/**
		Grabs keyboard focus into this component
	*/
	virtual void grabKeyboardFocus();

	/**
		Repaints the component
	*/
	virtual void repaint();

	virtual ~TComponent();

private:

	// implementation of getters & setters are moved into seperate functions to overcome a bug in Visual Studio!
	// https://stackoverflow.com/questions/57910453/declspecproperty-and-virtual-functions

	virtual TString getClassNameImpl();
	virtual HWND getHandleImpl();
	virtual TString getTextImpl();
	virtual void setTextImpl(const TString& caption);
	virtual HWND getParentImpl();
	virtual void setParentImpl(HWND parentHandle);
	virtual DWORD getStyleImpl();
	virtual void setStyleImpl(DWORD compStyle);
	virtual DWORD getExtendedStyleImpl();
	virtual void setExtendedStyleImpl(DWORD compExStyle);
	virtual int getLeftImpl();
	virtual void setLeftImpl(int compLeft);
	virtual int getTopImpl();
	virtual void setTopImpl(int compTop);
	virtual int getWidthImpl();
	virtual void setWidthImpl(int compWidth);
	virtual int getHeightImpl();
	virtual void setHeightImpl(int compHeight);
	virtual void setVisibleImpl(bool state);
	virtual bool getVisibleImpl();
	virtual bool getEnabledImpl();
	virtual void setEnabledImpl(bool state);
	virtual void setFontImpl(HFONT fontHandle);
	virtual HFONT getFontImpl();
	virtual void setCursorImpl(HCURSOR cursorHandle);
	virtual HCURSOR getCursorImpl();

	MCL_LEAK_DETECTOR(TComponent)
};
